#pragma once

#include <string>
#include <vector>
#include <mutex>
#include <iostream>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#include <unistd.h>
#include <curl/curl.h>

#define CURL_CONNECT_TIMEOUT 5
#define CURL_OPT_TIMEOUT 10

class http_header
{
public:
    std::string accept_key = "accept";
    std::string accept;
    std::string content_type_key = "content-type";
    std::string content_type;
    std::string token;
    std::string token_key;
    std::string sign;
    std::string sign_key;
    std::string time_stamp;
    std::string time_stamp_key;
};

enum request_type
{
    RT_GET,
    RT_POST,
    RT_DELETE,
};
namespace bm
{
// in the main() first to call
inline void init_curl()
{
    curl_global_init(CURL_GLOBAL_ALL);
}

// end the main() to call
inline void release_curl()
{
    curl_global_cleanup();
}

namespace curl_ftp
{

static size_t read_callback(void *ptr, size_t size, size_t nmemb, void *stream)
{
  curl_off_t nread;

  size_t retcode = fread(ptr, size, nmemb, (FILE*)stream);

  nread = (curl_off_t)retcode;

  fprintf(stderr, " We read %" CURL_FORMAT_CURL_OFF_T
          " bytes from file\n", nread);

  return retcode;
}

static void write_callback(void* data, size_t size, size_t nmemb, void* ptr) 
{
    fwrite(data, size, nmemb, stdout);
}

inline int ftp_upload(const std::string& path, const std::string& ftpurl, const std::string& dest_dir, const std::string& dest_name)
{
    /* 获得上传文件的大小 */
    struct stat file_info;
    curl_off_t fsize = 0;
    if(stat(path.c_str(), &file_info)) {
        printf("Couldn't open '%s': %d\n", path.c_str(), errno);

        return -2;
    }
    fsize = (curl_off_t)file_info.st_size;
    printf("Local file size: %" CURL_FORMAT_CURL_OFF_T " bytes.\n", fsize);
    /* 获得FILE类型变量 */
    auto hd_src = fopen(path.c_str(), "rb");

    CURL* curl = curl_easy_init();
    if(!curl)
        return -1;

    struct curl_slist *headers = nullptr;
    headers = curl_slist_append(headers, ("RNFR " + dest_name).c_str());
    // headers = curl_slist_append(headers, "RNTO renamed-and-fine.txt");
    curl_easy_setopt(curl, CURLOPT_READFUNCTION, read_callback);
    curl_easy_setopt(curl, CURLOPT_UPLOAD, 1L);
    curl_easy_setopt(curl, CURLOPT_URL, (ftpurl + dest_dir + dest_name).c_str());
    curl_easy_setopt(curl, CURLOPT_POSTQUOTE, headers);
    curl_easy_setopt(curl, CURLOPT_READDATA, hd_src);
    curl_easy_setopt(curl, CURLOPT_INFILESIZE_LARGE, (curl_off_t)fsize);
    auto res = curl_easy_perform(curl);
    
    if(res != CURLE_OK)
    {
        std::cout << path << " file upload error code = " << res << std::endl;
    }

    curl_slist_free_all(headers);
    curl_easy_cleanup(curl);

    return res;
}

inline int ftp_mkdir(const std::string& ftpurl, const std::string& parent_dir, const std::string& dirname)
{
    CURL* curl = curl_easy_init();
    if(!curl)
        return -1;

    struct curl_slist *headers = nullptr;
    headers = curl_slist_append(headers, ("MKD " + dirname).c_str());

    curl_easy_setopt(curl, CURLOPT_URL, (ftpurl + parent_dir).c_str());
	curl_easy_setopt(curl, CURLOPT_NOBODY, 1L);
    // curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L);
	// curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0L);
	// curl_easy_setopt(curl, CURLOPT_USE_SSL, CURLUSESSL_ALL);
    curl_easy_setopt(curl, CURLOPT_QUOTE, headers);
    auto res = curl_easy_perform(curl);
    if(res != CURLE_OK)
    {
        std::cout << dirname << " dir create error code = " << res << std::endl;
    }
    curl_slist_free_all(headers);
    curl_easy_cleanup(curl);

    return res;
}

inline int ftp_has_dir(const std::string& ftpurl, const std::string& dir)
{
    CURL* curl = curl_easy_init();
    if(!curl)
        return -1;

    curl_easy_setopt(curl, CURLOPT_URL, (ftpurl + dir).c_str());
    curl_easy_setopt(curl, CURLOPT_NOBODY, 1L);
    CURLcode res = curl_easy_perform(curl);

    curl_easy_cleanup(curl);

    return res;
}

inline int ftp_mkdirs(const std::string& ftpurl, const std::string& parent_dir, const std::string& dirnames)
{
    if(ftpurl.empty())
        return -1;

    auto split = [=](const std::string& str, const std::string& sp)->std::vector<std::string>
    {
        std::vector<std::string> result;
        std::string::size_type start = 0;
        std::string::size_type pos = 0;
        do
        {
            pos = str.find(sp, start);
            if(pos != std::string::npos)
            {
                result.push_back(str.substr(start, pos - start));
                start = pos + sp.size();
            }
            else
                result.push_back(str.substr(start));
        }
        while(pos != std::string::npos);

        return result;
    };
    
    std::vector<std::string> names = split(dirnames, "/");
    std::string dir;
    int res = 0;
    for(auto i : names)
    {
        if(i.empty())
            continue;
        dir += i + "/";
        if(ftp_has_dir(ftpurl, dir))
            res = ftp_mkdir(ftpurl, parent_dir, dir);

        if(res)
            break;
    }

    return res;
}

}

}

class curl_http
{
public:
    curl_http();
    std::string get(const std::string& url, const http_header& head);
    std::string post(const std::string& url, const std::string& data, const http_header& head);
    std::string del(const std::string& url, const http_header& head);
    ~curl_http();
private:
    std::string request(const std::string& url, const http_header& h, request_type t = RT_GET, const std::string& data = "");
private:
    // CURL* m_curl = nullptr;

    std::mutex m_lock;
};

namespace curl_ftp
{
int ftp_upload();
}